//
//  GameLogic.m
//  zhuan
//
//  Created by zhao ys on 5/27/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "GameLogic.h"
#import "playLayer.h"
#include <time.h>
#include <stdlib.h>
/*
class GameLogic
{
	int row_;
	int col_;
	int type_cnt_;
	int count_per_type_;
	int * field_;
	
public:
	GameLogic();
	~GameLogic();
	
	int gen(int row, int col, int type_cnt, int count_per_type);
	int valueAt(int x, int y);
};
*/

int score_ = 0;
int score_cur_ = 0;
int cur_level_ = 0;
float time_cost_ = 0;
float time_dec_ = 3.0f;
float hint_time_ = 5;
float time_max_ = 120;
float add[4] = {1, 2, 3, 5};
BOOL sound_on = YES;
BOOL music_on = YES;
BOOL game_center_on = YES;
BOOL auto_play_on = NO;

@implementation GameLogic
@synthesize row = row_;
@synthesize col = col_;
@synthesize types = type_cnt_;
@synthesize delegate;
@synthesize left = left_buttons_;
@synthesize time_left = time_left_;
int mode = 0;

+(void)ClassicMode {
	NSLog(@"set classic mode\n");
	mode = 0;
}

+(void)ScoreMode {
	NSLog(@"set score mode\n");
	mode = 1;
}

+(void)EndlessMode {
	NSLog(@"set endless mode\n");
	mode = 3;
}

+(BOOL)IsClassicMode {
	return mode == 0;
}

+(BOOL)IsEndlessMode {
	return mode == 3;
}

+(BOOL)IsScoreMode {
	return mode == 1;
}

+(void)ResetGameConfig {
	NSLog(@"ResetGameConfig mode:%d\n", mode);
	if (mode == 0) {
		add[0] = add[1] = add[2] = add[3] = 0;
		time_max_ = 180;
	} else {
		time_max_ = 120;
		add[0] = 1;
		add[1] = 2;
		add[2] = 3;
		add[3] = 5;
	}
	score_ = 0;
	score_cur_ = 0;
	time_cost_ = 0;
	hint_time_ = 5;
	cur_level_ = 0;
}

static GameLogic * sharedGameLogic_ = nil;
+(GameLogic*) sharedGameLogic {
	if (sharedGameLogic_ == nil) {
		sharedGameLogic_ = [[GameLogic alloc] init];
	}
	return sharedGameLogic_;
}

-(id) init {
	if ((self=[super init])) {
		row_ = col_ = type_cnt_ = count_per_type_ = 0;
		field_ = 0;
        field_org_ = 0;
		left_buttons_ = 0;
		delegate = nil;
	}
	return self;
}

-(void)dealloc {
	if (field_) {
		free(field_);
	}
	if (field_org_) {
		free(field_org_);
	}
	[super dealloc];
}

-(int)next_random_position:(int)free_space {
	assert(free_space <= row_ * col_);
	while (1) {
		int pos = rand() % free_space;
		for (int j = 0; j < row_ * col_; j++) {
			if (field_[j] == 0) {
				if (pos-- == 0)  {
					return j;
				}
			}
		}
	}
	return -1;
}

-(int) fill {
	int * left = malloc(sizeof(int) * (type_cnt_ + 1));
	for(int i = 0; i <= type_cnt_; i++) {
		left[i] = count_per_type_;
	}
	//left[0] = row_ * col_ - count_per_type_ * type_cnt_;

	int free_space = row_ * col_;
	srand(time(0));
	for (int i = 1; i <= type_cnt_; i++) {
		while (left[i]) {
			int pos = [self next_random_position:free_space];
			field_[pos] = i;
			if (self.delegate) {
				int x = pos % col_;
				int y = pos / col_;
				[self.delegate CreateButton:i X:x Y:y];
			}
			left[i] --;
			free_space--;
		}
	}
	left_buttons_ = type_cnt_ * count_per_type_;
	free(left);
	return 0;
}

-(int)gen:(int)row col:(int)col type_cnt:(int)type_cnt count_per_type:(int)count_per_type {
	if (row <= 0 || col <= 0 || type_cnt <= 0 || count_per_type <= 0) {
		return -1;
	}
	
	if ( type_cnt * count_per_type >= row * col) {
		return -1;
	}
	
	NSLog(@"GameLogic gen: %dx%d  %d@%d\n", col, row, type_cnt, count_per_type);

	cur_level_ ++;
	row_ = row;
	col_ = col;
	type_cnt_ = type_cnt;
	count_per_type_ = count_per_type;
    time_left_ = time_max_;
	
	if (field_) {
		free(field_);
	}
	if (field_org_) {
		free(field_org_);
	}
	
	field_ = malloc(sizeof(int) * row_ * col_); //    sizeof(int) * row * col);
	for(int i = 0; i < row_ * col_; i ++) {
		field_[i] = 0;
	}
	
	field_org_ = malloc(sizeof(int) * row_ * col_);

	int ret = [self fill];
	memcpy(field_org_, field_, sizeof(int) * row_ * col_);
	start_score_ = score_;
	return ret;
}

-(int)gen {
	int row = 15;
	int col = 23;
	return [self gen:row col:col type_cnt:10 count_per_type:20];	
}


-(NSArray*)grow:(int)grow_cnt {
	int * cnt_of_type = malloc(sizeof(int) * (type_cnt_ + 1));
	memset(cnt_of_type, 0, sizeof(int) * (type_cnt_ + 1));
	int used = 0;
	for(int i = 0; i < row_ * col_; i++) {
		if (field_[i] > 0) {
			cnt_of_type[field_[i]]++;
			used ++;
		}
	}
	
	NSMutableArray * array = [NSMutableArray array];
	while(grow_cnt > 0) {
		int free_space = row_ * col_ - used;
		assert(grow_cnt <= free_space);
		
		int min_cnt = 99999;
		int min_type = -1;
		for(int i = 1; i <= type_cnt_; i++) {
			if(cnt_of_type[i] < min_cnt) {
				min_cnt = cnt_of_type[i];
				min_type = i;
			}
		}
		
		assert(min_type > 0 && min_type <= type_cnt_);
		int pos = [self next_random_position:free_space];
		assert(pos >=0 && pos < row_ * col_);
		int x = pos % col_;
		int y = pos / col_;
		
		field_[pos] = min_type;
		if (self.delegate) {
			[self.delegate CreateButton:min_type X:x Y:y];
		}
		[array addObject:[NSNumber numberWithInt:(pos * 1000 + min_type)]];
		
		cnt_of_type[min_type]++;
		used++;
		grow_cnt--;
		left_buttons_++;
	}
	free(cnt_of_type);
	return array;
}

-(void) check_grow {
	if (mode == 3) {
		if( ![self haveStep] || left_buttons_ <= type_cnt_ * count_per_type_ / 3) {
			[self grow:((type_cnt_ * count_per_type_ * 3 / 4) - left_buttons_)];
			for(int i = 0; i < 4; i++) {
				add[i] *= 0.8; //{1, 2, 3, 5}
			}
			[self.delegate AddTime];
		}
	}
}

-(int)valueAt:(int)x Y:(int)y {
	if (field_ == 0 ||
		x < 0 || x >= col_ ||
		y < 0 || y >= row_ ) {
		return -1;
	}
	return field_[y * col_ + x];
}


-(int)remove:(int)x Y:(int)y {
	if ([self valueAt:x Y:y] <= 0) {
		return -1;
	}
	
	int pos = y * col_ + x;
	int type = field_[pos];
	field_[pos] = 0;
	left_buttons_ --;
	return (pos * 1000 + type);
}

-(NSMutableArray*) HitTest:(int)x Y:(int)y {
	if (x < 0 || x >= col_ ||
		y < 0 || y >= row_) {
		return nil;
	}

	if (field_[y * col_ + x] != 0) {
		return nil;
	}

	NSMutableArray * array = [NSMutableArray arrayWithObjects:nil];
	for (int i = 0; i < type_cnt_+1; i++) {
		[array insertObject:[NSMutableArray arrayWithObjects:nil] atIndex:i]; 
	}

#define ADD_POS_AND_BREAK(pos) \
	int type = field_[pos]; \
	if (type != 0) { \
		NSMutableArray * a = [array objectAtIndex:type]; \
		[a addObject:[NSNumber numberWithInt:(pos * 1000 + type)]]; \
		break; \
	}

	for (int i = x - 1; i >= 0; i--) {
		int pos = y * col_ + i;
		ADD_POS_AND_BREAK(pos);
	}
	
	for (int i = x + 1; i < col_; i++) {
		int pos = y * col_ + i;
		ADD_POS_AND_BREAK(pos);
	}
	
	for (int i = y - 1; i >= 0; i--) {
		int pos = i * col_ + x;
		ADD_POS_AND_BREAK(pos);
	}
	
	for (int i = y + 1; i < row_; i++) {
		int pos = i * col_ + x;
		ADD_POS_AND_BREAK(pos);
	}
	
	NSMutableArray * ret = [NSMutableArray arrayWithObjects:nil];
	for (NSMutableArray * a in array) {
		if (a.count < 2) {
			continue;
		}

		for (NSNumber * n in a) {
			[ret addObject:n];
		}
	}
	return ret;
}

-(NSMutableArray*) hit:(int)x Y:(int)y {
	NSMutableArray * array = [self HitTest:x Y:y];
	if (array) {
		for (NSNumber * n in array ) {
			int pos = [n intValue] / 1000;
			field_[pos] = 0;
		}
	}
	
	if (array && array.count > 0) {
		left_buttons_ -= array.count;
		[self check_grow];
	}
	return array;
}

-(NSMutableArray*) pathOf:(int)x Y:(int)y  dest:(int)dest
{
	int desc_x = dest % col_;
	int desc_y = dest / col_;

	NSMutableArray * array = nil;
	if (x == desc_x && y == desc_y) {
	} else if (x == desc_x) {
		array = [NSMutableArray arrayWithObjects:nil];
		int i = MIN(y, desc_y), end = MAX(y, desc_y);
		for(; i <= end; i++) {
			[array addObject:[NSNumber numberWithInt: i * col_ + x]];
		}
	} else if (y == desc_y) {
		array = [NSMutableArray arrayWithObjects:nil];
		int i = MIN(x, desc_x), end = MAX(x, desc_x);
		for(; i <= end; i++) {
			[array addObject:[NSNumber numberWithInt: y * col_ + i]];
		}
	} else {
	}
	return array;
}

-(BOOL) haveStep {
	return ([self nextStep] != -1);
}

-(int) nextStep {
	int x, y;
	for (x = 0; x < col_; x++) {
		for (y = 0; y < row_; y++) {
			NSMutableArray* array = [self HitTest:x Y:y];
			if (array && array.count > 0) {
				return y * col_ + x;
			}
		}
	}
	return -1;
}

-(void)addTime:(float)t {
    time_left_ += t;
    if(time_left_ < 0) {
        time_left_ = 0;
    }
    
    if(time_left_ > time_max_) {
        time_left_ = time_max_;
    }
}

-(int)Restart {
	NSLog(@"GameLogic restart\n");
	score_cur_ = 0;
	time_cost_ = 0;
	score_ = start_score_;
	memcpy(field_, field_org_, sizeof(int) * row_ * col_);
	left_buttons_ = 0;
	for (int i = 0; i < row_ * col_; i++) {
		if (field_[i] > 0) {
			left_buttons_++;
		}
	}
    time_left_ = time_max_;
	return 0;
}

struct stGameConfig {
    int save_type;
    int row_;
	int col_;
	int type_cnt_;
	int count_per_type_;

	int left_buttons_;
	int start_score_;
    
    int mode;
    int score_;
    int score_cur_;
    int cur_level_;
    float time_left_;
    float time_cost_;
    float time_dec_;
    float hint_time_;
    float time_max_;
    float add[4];
    
    int * field_;
	int * field_org_;
};

-(NSString*) HexEncode:(char*)data len:(int)data_len {
    char * out_data = malloc(data_len*2+1);
    memset(out_data, 0, data_len * 2 + 1);
    for(int i = 0; i < data_len; i++) {
        sprintf(out_data + i * 2, "%02X", (unsigned char)data[i]);
    }
    NSString * str = [NSString stringWithFormat:@"%s", out_data];
    free(out_data);
    return str;
}

int hex2int(char hex) {
    if (hex >= '0' && hex <= '9') {
        return hex - '0';
    }
    
    if (hex >= 'a' && hex <= 'f') {
        return hex - 'a' + 10;
    }
    
    if (hex >= 'A' && hex <= 'F') {
        return hex - 'A' + 10;
    }
    return -1;
}

-(int) HexDecode:(NSString*)str data:(char*)data len:(int)data_len {
    if(str.length % 2 != 0) {
        return -1;
    }
    
    if (str.length / 2 != data_len) {
        return -1;
    }
    
    for (int i = 0; i < str.length; i+=2) {
        char c1 = [str characterAtIndex:i];
        int h1 = hex2int(c1);
        if(h1 < 0) {return -1;}
        
        char c2 = [str characterAtIndex:i+1];
        int h2 = hex2int(c2);
        if(h2 < 0) {return -1;}
        
        data[i/2] = h1 * 16 + h2;
    }
    return str.length / 2;
}


-(BOOL) checkEncode:(NSString*)str data:(char*)data len:(int)data_len {
    char * mem = malloc(data_len);
    if([self HexDecode:str data:mem len:data_len] != data_len) {
        free(mem);
        return FALSE;
    }
    
    for (int i = 0; i < data_len; i++) {
        if(mem[i] != data[i]) {
            free(mem);
            return FALSE;
        }
    }
    free(mem);
    return TRUE;
}

-(void)save {
    //[self cleansave];
    
    struct stGameConfig gc;
    gc.row_ = row_;
    gc.col_ = col_;
	gc.type_cnt_ = type_cnt_;
	gc.count_per_type_ = count_per_type_;

	gc.left_buttons_ = left_buttons_;
	gc.start_score_ = start_score_;
    gc.time_left_ = time_left_;
    gc.mode = mode;
    gc.score_ = score_;
    gc.score_cur_ = score_cur_;
    gc.cur_level_ = cur_level_;
    gc.time_cost_ = time_cost_;
    gc.time_dec_ = time_dec_;
    gc.hint_time_ = hint_time_;
    gc.time_max_ = time_max_;
    gc.add[0] = add[0];
    gc.add[1] = add[1];
    gc.add[2] = add[2];
    gc.add[3] = add[3];

    size_t field_size = sizeof(int) * row_ * col_;
    //NSString * saveStr = [self encode:(char*)&gc len:sizeof(gc)];
    //NSMutableData * data = [NSMutableData dataWithBytes:&gc length:sizeof(gc)];
    
    NSMutableDictionary * save = [NSMutableDictionary dictionary];
    
    NSString * stat =  [self HexEncode:(char*)&gc len:sizeof(gc)];
    NSString * field = [self HexEncode:(char*)field_ len:field_size];
    NSString * field_org = [self HexEncode:(char*)field_org_ len:field_size];

    if(![self checkEncode:stat data:(char*)&gc len:sizeof(gc)]) {
        return;
    }
    
    if(![self checkEncode:field data:(char*)field_ len:field_size]) {
        return;
    }

    
    if(![self checkEncode:field_org data:(char*)field_org_ len:field_size]) {
        return;
    }

    NSLog(@"stat = [%@]\n", stat);
    NSLog(@"field = [%@]\n", field);
    NSLog(@"field_org = [%@]\n", field_org);
    
    [save setValue:stat forKey:@"stat"];
    [save setValue:field forKey:@"field"];
    [save setValue:field_org forKey:@"field_org"];
    
    NSString *path = [CCFileUtils fullPathFromRelativePath:@"game_config.plist"];
	//NSMutableDictionary * dict =[[NSMutableDictionary alloc] initWithContentsOfFile:path];
    NSMutableDictionary * dict =[NSMutableDictionary dictionaryWithContentsOfFile:path];

    [dict setValue:save forKey:@"save"];

    [dict writeToFile:path atomically:YES];
	//[dict release];
}

-(void)cleansave {
    NSString *path = [CCFileUtils fullPathFromRelativePath:@"game_config.plist"];
	//NSMutableDictionary * dict =[[NSMutableDictionary alloc] initWithContentsOfFile:path];
    NSMutableDictionary * dict =[NSMutableDictionary dictionaryWithContentsOfFile:path];

    [dict removeObjectForKey:@"save"];
    
    [dict writeToFile:path atomically:YES];
	//[dict release];
}

-(BOOL) havesave {
    NSString *path = [CCFileUtils fullPathFromRelativePath:@"game_config.plist"];
	//NSDictionary * dict =[[NSDictionary alloc] initWithContentsOfFile:path];
    NSDictionary * dict =[NSDictionary dictionaryWithContentsOfFile:path];
    NSMutableDictionary * save = [dict objectForKey:@"save"];
    
    if(save == nil) {
//        [dict release];
        return FALSE;
    }

    NSString * stat = [save objectForKey:@"stat"];
    NSString * field = [save objectForKey:@"field"];
    NSString * field_org = [save objectForKey:@"field_org"];
    
    if(stat == nil || field == nil || field_org == nil) {
//        [dict release];
        return FALSE;
    }
//    [dict release];
    return TRUE;
 /*   
    struct stGameConfig gc;
    	
    if (stat == nil || stat.length < sizeof(gc) * 2) {
        [dict release];
        return FALSE;
    }
    
    
    if([self HexDecode:stat data:(char*)&gc len:sizeof(gc)]!= sizeof(gc)) {
        [dict release];
        return FALSE;
    }

    size_t field_size = sizeof(int) * gc.row_ * gc.col_;
    char * xfield = malloc(field_size);
    if( [self HexDecode:field data:xfield len:field_size] != field_size) {
        free(xfield);
        [dict release];
        return FALSE;
    }
    
    if( [self HexDecode:field_org data:xfield len:field_size] != field_size) {
        free(xfield);
        [dict release];
        return FALSE;
    }
    free(xfield);
    
    return TRUE;
*/
}

-(int)load {
    NSString *path = [CCFileUtils fullPathFromRelativePath:@"game_config.plist"];
	NSDictionary * dict =[NSDictionary dictionaryWithContentsOfFile:path];

    NSDictionary * save = [dict objectForKey:@"save"];
    NSString * stat = [save objectForKey:@"stat"];
    NSString * field = [save objectForKey:@"field"];
    NSString * field_org = [save objectForKey:@"field_org"];
    
    struct stGameConfig gc;

    if (stat == nil || stat.length < sizeof(gc) * 2) {
        return -1;
    }
    
    if([self HexDecode:stat data:(char*)&gc len:sizeof(gc)]!= sizeof(gc)) {
        return -1;
    }

    size_t field_size = sizeof(int) * gc.row_ * gc.col_;
    
    int * xfield_ = malloc(field_size);

    if( [self HexDecode:field data:(char*)xfield_ len:field_size] != field_size) {
        free(xfield_);
        return -1;
    }
    
    int * xfield_org_ = malloc(field_size);
    if( [self HexDecode:field_org data:(char*)xfield_org_ len:field_size] != field_size) {
        free(xfield_);
        free(xfield_org_);
        return -1;
    }
    
    if (field_) {
        free(field_);
    }
    
    field_ = xfield_;
    if (field_org_) {
        free(field_org_);
    }
    field_org_ = xfield_org_;

    row_ = gc.row_;
    col_ = gc.col_;
	type_cnt_ = gc.type_cnt_;
	count_per_type_ = gc.count_per_type_;
    
	left_buttons_ = gc.left_buttons_;
	start_score_ = gc.start_score_;
    time_left_ = gc.time_left_;
    mode = gc.mode;
    score_ = gc.score_;
    score_cur_ = gc.score_cur_;
    cur_level_ = gc.cur_level_;
    time_cost_ = gc.time_cost_;
    time_dec_ = gc.time_dec_;
    hint_time_ = gc.hint_time_;
    time_max_ = gc.time_max_;
    add[0] = gc.add[0];
    add[1] = gc.add[1];
    add[2] = gc.add[2];
    add[3] = gc.add[3];
    
    if(time_left_ <= 0.01) {
        for(int i = 0; i < 4; i++) {
            add[i] *= 0.8; //{1, 2, 3, 5}
        }
        
        if ([GameLogic IsClassicMode]) {
            [GameLogic ResetGameConfig];
        }
        [[GameLogic sharedGameLogic] gen];	
    }
    return 0;
}


@end
